package alocador.entidades;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

import alocador.exceptions.*;
import alocador.util.VerificaInvalido;

/**
 * O pacote entidades diz respeito as entidades do Sistema, ou seja, Evento e
 * Horario Classe que modela a Entidade Horario. Trata os Horario de um Evento
 */

public class Horario {

	private GregorianCalendar dataInicio;
	private GregorianCalendar dataFim;
	private final static int ULTIMO_DIA_DO_MES = 30;
	private final static int SEMANA = 7;
	// O gregorian Calendar trata os meses de 0 a 11, ou seja, Janeiro e 0 e
	// Dezembro e 11. Como usuario passa os valores
	// em notacao normal (1 a 12), temos que subtrair um.
	private final static int SINCRONIZA_COM_GREGORIAN_CALENDAR = 1;

	// construtor
	public Horario(String dataComeco, String dataTermino)
			throws DataInvalidaException {

		if (VerificaInvalido.basico(dataComeco)
				|| VerificaInvalido.basico(dataTermino))
			throw new DataInvalidaException("Intervalo de datas invalido.");

		this.dataInicio = criaHorario(dataComeco);
		this.dataFim = criaHorario(dataTermino);

		if (VerificaInvalido.dataInvalida(dataInicio, dataFim))
			throw new DataInvalidaException("Intervalo de datas invalido.");

	}

	// construtor
	public Horario(GregorianCalendar dataComeco, GregorianCalendar dataTermino) {
		this.dataInicio = dataComeco;
		this.dataFim = dataTermino;
	}

	/**
	 * Retorna a Data de Inicio do Evento
	 * @return Data de Inicio do Evento
	 */
	public GregorianCalendar getDataInicio() {
		return this.dataInicio;
	}

	/**
	 * Modifica a Data Inicial do Evento
	 * @param novaDataInicio
	 *            Nova Data Inicial do Evento
	 * @throws DataInvalidaException
	 *             Verifica se a data e invalida, 30/02, por exemplo
	 */
	public void setDataInicio(String novaDataInicio)
			throws DataInvalidaException {
		this.dataInicio = criaHorario(novaDataInicio);
	}

	/**
	 * Retorna a Data de Termino do Evento
	 * @return Data de Termino do Evento
	 */
	public GregorianCalendar getDataFim() {
		return this.dataFim;
	}

	/**
	 * Modifica a Data Final do Evento
	 * @param novaDataFim
	 *            Nova Data Final do Evento
	 * @throws DataInvalidaException
	 *             Verifica se a data e invalida, 30/02, por exemplo
	 */
	public void setDataFim(String novaDataFim) throws DataInvalidaException {
		this.dataFim = criaHorario(novaDataFim);
	}

	/**
	 * Trata a data de Inicio e Termino do Evento
	 * @param data
	 *            Data do Evento
	 * @param atributo
	 *            Data de Inicio ou Termino
	 * @return Horario no formato de Gregorian Calendar
	 * @throws DataInvalidaException
	 *             Verifica se a data e invalida, 30/02, por exemplo
	 */
	public GregorianCalendar criaHorario(String data)
			throws DataInvalidaException {

		String split[] = data.trim().split(" ");

		String date = split[0];
		String horario = split[1];

		String dataCompleta[] = date.trim().split("/");
		int dia = Integer.parseInt(dataCompleta[0]);
		int mes = Integer.parseInt(dataCompleta[1])
				- SINCRONIZA_COM_GREGORIAN_CALENDAR;
		int ano = Integer.parseInt(dataCompleta[2]);

		String horarioCompleto[] = horario.trim().split(":");
		int hora = Integer.parseInt(horarioCompleto[0]);
		int minutos = Integer.parseInt(horarioCompleto[1]);

		GregorianCalendar gc = new GregorianCalendar(ano, mes, dia, hora,
				minutos);

		if (mes == Calendar.FEBRUARY && dia >= ULTIMO_DIA_DO_MES)
			throw new DataInvalidaException("Intervalo de datas invalido.");

		return gc;

	}

	/**
	 * Verifica se um horario tem choque com um horario de outro evento
	 * @param horario
	 *            Horario de um evento
	 * @return True, caso haja conflito. Caso contrario, false.
	 * @throws DataInvalidaException
	 *             Verifica se a data e invalida, 30/02, por exemplo
	 */
	public boolean temChoqueHorario(String horario)
			throws DataInvalidaException {
		GregorianCalendar data = criaHorario(horario);
		if (data.compareTo(getDataInicio()) >= 0
				&& data.compareTo(getDataFim()) <= 0)
			return true;
		return false;
	}

	/**
	 * Verifica se Dois eventos dao conflito de Horario
	 * @param horario
	 *            Horario do Evento a ser verificado Conflito
	 * @return True, se eles dao conflito. False, caso contrario
	 */
	public boolean temChoqueHorario(Horario horario) {

		// Eventos num mesmo horario
		if (horario.getDataInicio().compareTo(getDataInicio()) == 0
				&& horario.getDataFim().compareTo(getDataFim()) == 0) {
			return true;
		}

		// Evento B dentro de Evento A
		if (horario.getDataInicio().compareTo(getDataInicio()) >= 0
				&& horario.getDataFim().compareTo(getDataFim()) <= 0) {
			return true;
		}

		// Evento B comeca entre o inicio e o fim do Evento A e termina depois
		if (horario.getDataInicio().compareTo(getDataInicio()) >= 0
				&& horario.getDataInicio().compareTo(getDataFim()) < 0
				&& horario.getDataFim().compareTo(getDataFim()) > 0) {
			return true;
		}

		// Evento B comeca antes do evento A e termina entre o evento A
		if (horario.getDataInicio().compareTo(getDataInicio()) < 0
				&& horario.getDataFim().compareTo(getDataInicio()) > 0
				&& horario.getDataFim().compareTo(getDataFim()) <= 0) {
			return true;
		}

		return false;

	}

	/**
	 * Verifica se um horario ja esta contido no Evento
	 * @param data
	 *            Data a ser verifica se ja existe
	 * @return True, caso exista. False, caso contrario
	 * @throws DataInvalidaException
	 *             Verifica se a data e invalida, 30/02, por exemplo
	 */
	public boolean contemHorario(String data) throws DataInvalidaException {

		GregorianCalendar horario = criaHorario(data);

		if (horario.compareTo(getDataInicio()) >= 0
				&& horario.compareTo(getDataFim()) <= 0) {
			return true;
		}

		return false;
	}

	/**
	 * Em eventos repetitivos, avanca uma semana na data original
	 * @param horario
	 *            Horario original
	 * @return Horario + "1 semana"
	 */
	public static Horario avancaSemana(Horario horario) {
		GregorianCalendar novoDataInicio = (GregorianCalendar) horario
				.getDataInicio().clone();
		GregorianCalendar novoDataFim = (GregorianCalendar) horario
				.getDataFim().clone();

		novoDataInicio.add(Calendar.DATE, SEMANA);
		novoDataFim.add(Calendar.DATE, SEMANA);
		Horario temp = new Horario(novoDataInicio, novoDataFim);
		return temp;

	}

	/**
	 * Verifica se dois horarios sao iguais
	 * @return True, caso os objetos sejam iguais. False, caso contrario
	 */
	@Override
	public boolean equals(Object obj) {
		if ((obj == null) || !(obj instanceof Horario))
			return false;

		Horario other = (Horario) obj;

		return (getDataInicio().equals(other.getDataInicio()) && getDataFim()
				.equals(other.getDataFim()));
	}

	/**
	 * Retorna a representacao do horario
	 * @return Representacao do Horario de um Evento
	 */
	@Override
	public String toString() {

		SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm");
		String dataInicioStr = formatter.format(getDataInicio().getTime());
		String dataFimStr = formatter.format(getDataFim().getTime());

		return "Data Inicio: " + dataInicioStr + " - " + "Data Fim: "
				+ dataFimStr;

	}

}
